#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#include "libcfg.h"

/*
 * cfg_parse - parse a cofig file
 *
 * @file name of the config file to be parsed
 * @items config options supported, should be %NULL terminated
 *
 * return 0 on success, -1 on error
 */
int cfg_parse(char *file, cfg_item_t *items)
{
  FILE *fp;
  char *line;
  size_t len;
  int lnum = 0;
  cfg_item_t *it;

  assert(file && items);
  
  fp = fopen(file, "r");
  if (!fp) {
    perror("fopen");
    return -1;
  }

  /* loop for every single line */
  while(1) {
    int inquote = 0;
    char *ptr, *name, *sign, *val;

    /* get one line */
    line = fgetln(fp, &len);
    if (!line) {
      if (feof(fp)) { /* normal ending */
	break;
      } else {      /* must be ferror() */
	perror("fgetln");
	fclose(fp);
	return -1;
      }
    }

    /* update line number */
    lnum++;

    /* caculate string length */
    if (line[len-1] == '\n') {
      len -= 1;
    }

    /* eat leading blanks */
    for (; len && isspace(*line); line++, len--);

    /* skip comments and empty lines */
    if ( !len || *line == '#' ) {
      continue;
    }

    val = sign = NULL;
    name = ptr = line;
    while (len) {
      if (*line == '\\') {
	line++; len--;

	if (!len) {
	  fprintf(stderr, "%s: line %d: missing character after ending '\\'.\n", file, lnum);
	  fclose(fp);
	  return -1;
	}

	*ptr++ = *line;
      } else {
	if (inquote) {
	  if (*line == inquote) {
	    inquote = 0;
	  } else {
	    *ptr++ = *line;
	  }
	} else {
	  if (isspace(*line)) {
	    if (!sign) { /* so haven't meet a '=' yet */
	      for(line++, len--; len && isspace(*line); line++, len--);
	      if (*line != '=') {
		fprintf(stderr, "%s: line %d: invalid option name, missing quote?\n", file, lnum);
		fclose(fp);
		return -1;
	      }
	      line--; len++;
	    } else if (val) {
	      for(line++, len--; len && isspace(*line); line++, len--);
	      if (len) {
		fprintf(stderr, "%s: line %d: invalid option value, missing quote?\n", file, lnum);
		fclose(fp);
		return -1;
	      }
	    } else {
	      /* ignore */
	    }
	  } else if (*line == '=') {
	    if (sign) {
	      fprintf(stderr, "%s: line %d: too many '='.\n", file, lnum);
	      fclose(fp);
	      return -1;
	    } else {
	      *ptr++ = 0;
	      sign = line; /* we just care bool meaning of it */
	    }
	  } else {
	    if (sign && !val) {
	      val = ptr;
	    }

	    if (*line == '"' || *line == '\'') {
	      inquote = *line;
	    } else {
	      *ptr++ = *line;
	    }
	  }
	}
      }

      line++; len--;
    }
    *ptr = 0;

    if (inquote) {
      fprintf(stderr, "%s: missing ending quote.\n", file);
      fclose(fp);
      return -1;
    }

    /* save option values */
    for (it=items; it->name; it++) {
      if (strcmp(it->name, name) != 0) {
	continue;
      }

      if (it->type == CFG_NUM) {
	*(int *)(it->value) = strtol(val, NULL, 0);
      } else { /* treat as CFG_STR */
	*(char**)(it->value) = strdup(val);
	if (it->value == NULL) {
	  perror("strdup");
	  fclose(fp);
	  return -1;
	}
      }

      break;
    }

    /* no such options */
    if (!it) {
      fprintf(stderr, "%s: line %d: unsupported options.\n", file, lnum);
      fclose(fp);
      return -1;
    }
  }

  fclose(fp);
  return 0;
}
